#!/bin/bash
set -uo pipefail
#==============================================================#
# File      :   pgb-user
# Desc      :   add user to pgbouncer pool
# Ctime     :   2021-03-29
# Mtime     :   2024-08-05
# Path      :   /pg/bin/pgb-user
# Deps      :   psql, sed
# License   :   AGPLv3 @ https://pigsty.io/docs/about/license
# Copyright :   2018-2025  Ruohang Feng / Vonng (rh@vonng.com)
#==============================================================#
PROG_NAME="$(basename $0)"
PROG_DIR="$(cd $(dirname $0) && pwd)"

PATH=/usr/pgsql/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:$PATH
#--------------------------------------------------------------#
# use AUTO or "" as password will fetch from database
# use NULL as password will set empty password ""
# enc method will be fetched from password_encryption parameter
# md5 passwords will be calc by pg md5 salt algorithm
# scram pwd cannot be calculated, use plain-text instead
#--------------------------------------------------------------#


#--------------------------------------------------------------#
# Test Cases
# pgb-user dbp_vonng
# pgb-user dbp_vonng auto
# pgb-user dbp_vonngNE auto  # Not Exist Error
# pgb-user dbp_vonng null
# pgb-user dbp_vonng md596bceae83ba2937778af09adf00ae738
# pgb-user dbp_vonng md596bceae83ba29377
# pgb-user dbp_vonng Test.Password
#--------------------------------------------------------------#


#--------------------------------------------------------------#
# Name: pgbouncer_create_user
# Desc: Add or modify pgbouncer userlist
# Arg1: username (required)
# Arg2: password (optional)
# Note: Run this as dbsu (postgres)
#--------------------------------------------------------------#
function pgbouncer_create_user() {
    local username=$1
    local password=${2-''}
    local userlist=${3-'/etc/pgbouncer/userlist.txt'}
    local entry="\"${username}\" \"\""   # "username" ""
    mkdir -p /etc/pgbouncer/
    touch ${userlist}

    case ${password} in
    NULL | null) # force empty password
        entry="\"${username}\" \"\""     # "username" ""
        echo "use nil md5pwd: ${entry}"
        ;;
    AUTO | auto | "") # fetch db password
        entry=$(psql -AXtwq -U postgres -d postgres -c "SELECT concat('\"', rolname, '\" \"', rolpassword, '\"') FROM pg_authid WHERE rolname = '${username}'")
        if [[ ! -z ${entry} ]]; then
            echo "pwd fetched via postgres: ${entry}"
        else
            echo "pwd fetching failed for ${username} ${password}"
            exit 1
        fi
        ;;
    *)
        pwdenc=$(psql -AXtwq -U postgres -d postgres -c "SHOW password_encryption;" 2>/dev/null)
        case $pwdenc in
        md5)
            if [[ ${password} == md5* ]] && [[ ${#password} == 35 ]]; then # just use the md5password
                entry="\"${username}\" \"${password}\""
                echo "md5pwd given: ${entry}"
            else
                local md5_mon_pass="md5$(echo -n "${password}${username}" | md5sum | awk '{print $1}')"
                entry="\"${username}\" \"${md5_mon_pass}\""
                echo "md5pwd calculated: ${entry}"
            fi
            ;;
        scram-sha-256)
            entry="\"${username}\" \"${password}\""
            echo "SCRAM pwd cannot be calculated, use AUTO to fetch from postgres"
            ;;
        esac
        ;;
    esac

    # if username already exists
    if grep -q ${username} ${userlist}; then
        sed -i "/^\"${username}\"[[:blank:]]*.*$/d" ${userlist}
    fi
    # write "username" "password" to userlist
    echo "${entry}" >> ${userlist};
    cat ${userlist} | sort -r | uniq > "${userlist}e";
    mv -f "${userlist}e" "${userlist}"
}


#--------------------------------------------------------------#
#                             Main                             #
#--------------------------------------------------------------#
pgbouncer_create_user $@